﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Server.Model;
using Server.DB;
using Server.Controller.Graph;

namespace Server.Controller
{
    class PlanRouteController: IPlanRouteController
    {
        object Locker = new object();
        //StationRepository stationRep;
        //BatteryRepository batteryRep;
        //RouteRepository routeRep;
        //PartReservationRepository partReservationRep;
        //ReservationRepository reservationRep;
        IRepository<Station> stationRepository;
        IRepository<Battery> batteryRepository;
        IRepository<Route> routeRepository;
        IRepository<PartReservation> partReservationRepository;
        IRepository<Reservation> reservationRepository;
        IGraphAlgoritme graph;

        public PlanRouteController(Context db)
        {
            //stationRep = new StationRepository(db);
            //batteryRep = new BatteryRepository(db);
            //routeRep = new RouteRepository(db);
            //partReservationRep = new PartReservationRepository(db);
            //reservationRep = new ReservationRepository(db);
            stationRepository = new Repository<Station>(db);
            batteryRepository = new Repository<Battery>(db);
            routeRepository = new Repository<Route>(db);
            partReservationRepository = new Repository<PartReservation>(db);
            reservationRepository = new Repository<Reservation>(db);
            graph = new GraphAlgoritme(GraphMode.UniDirectional);
        }

        public ICollection<Station> GetAllStations()
        {
            return stationRepository.All.ToList();
        }

        public ICollection<Station> PlanRoute(Station start, Station end, DateTime date, CarModel carModel, BatteryType batteryType)
        {
            //foreach (Station s in stationRep.All)
            foreach (Station s in stationRepository.All)
            {
                //if (batteryRep.GetBatteriesOnStation(s, batteryType).ToList().Count - partReservationRep.AllPartReservationOnStation(s, date).ToList().Count > 0)
                if(batteryRepository.SearchFor(b => b.Station.Id.Equals(s.Id) && b.BatteryType.Id.Equals(batteryType.Id)).Count() - 
                    partReservationRepository.SearchFor(p => p.Station.Id.Equals(s.Id) && p.Reservation.Date.Equals(date)).Count() > 0)
                {
                    graph.addStation(s);
                }
            }
            
            if(graph.getVertexById(start.Id) == null || graph.getVertexById(end.Id) == null)
            {
                throw new Exception("Endpoint not reachable!");
            }

            foreach (Route r in routeRepository.All)
            {
                if (r.Distance <= carModel.Range)
                {
                    graph.addRoute(r.From.Id,r.To.Id,r.Distance, r.Time);
                }
            }

            return graph.Dijkstras(graph.getVertexById(start.Id), graph.getVertexById(end.Id));            
        }

        public Reservation ComfirmRoute(User user, Car car, ICollection<Station> stations, DateTime date)
        {
            Reservation reservation = new Reservation(user, car);
            reservation.Date = date;
            
            //Critical begin
            lock (Locker)
            {
                foreach (Station station in stations)
                {
                    //if (batteryRep.GetBatteriesOnStation(station, car.CarModel.BatteryType).ToList().Count - partReservationRep.AllPartReservationOnStation(station, date).ToList().Count > 0)
                    if (batteryRepository.SearchFor(b => b.Station.Id.Equals(station.Id) && b.BatteryType.Id.Equals(car.CarModel.BatteryType.Id)).Count() -
                        partReservationRepository.SearchFor(p => p.Station.Id.Equals(station.Id) && p.Reservation.Date.Equals(date)).Count() > 0)
                    {
                        reservation.PartReservations.Add(new PartReservation(/*date, */reservation, car.CarModel.BatteryType, station));
                    }
                    else
                    {
                        throw new Exception("Ingen ledige batterier på stationen");
                    }
                }
                reservationRepository.Insert(reservation);
                reservationRepository.Save();
            }
            //Critical end
            return reservation;
        }
    }
}
